﻿using System;
using System.Data.Common;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Centido.Core.Test
{
    /// <summary>
    ///   Testing when unit level security is turned off.
    /// </summary>
    [TestClass]
    public class UnitSecurityTurnedOff
    {
        /// <summary>
        /// Domain name
        /// </summary>
        public const string DomainName = "FullAdminRights";

        /// <summary>
        ///   Domain.
        /// </summary>
        private Domain _domain;

        /// <summary>
        ///   The user.
        /// </summary>
        private User _user;

        /// <summary>
        ///   User table.
        /// </summary>
        private Table _userTable;

        /// <summary>
        ///   Table.
        /// </summary>
        private Table _table;

        /// <summary>
        /// Test initialize
        /// </summary>
        [TestInitialize]
        public void TestInitialize()
        {
            _domain = Domain.Build(DomainConfiguration.Load(DomainName));
            _domain.HasRecycleBin = new Random(Environment.TickCount).Next() % 2 == 1;

            _domain.DeleteAllUnits();
            _domain.DeleteAllRoles();
            _domain.DeleteAllUsers();

            _table = _domain.AddTable(StringProperty.RandomIdentifier());

            _table.GrantAll();

            _table.InsertRow();

            var userPassword = StringProperty.RandomPassword();
            _user = _domain.AddUser(StringProperty.RandomIdentifier(), userPassword);

            _userTable = _domain.Logon(_user.Name, userPassword).GetTable(_table.Name);
        }


        /// <summary>
        ///   Reading and writing the property that turns unit security off.
        /// </summary>
        [TestMethod]
        public void ReadingAndWritingTheProperty()
        {
            Assert.IsTrue(_table.HasUnitLevelSecurity);

            _table.HasUnitLevelSecurity = false;
            _table.HasUnitLevelSecurity = false;

            Assert.IsFalse(_table.HasUnitLevelSecurity);

            _table.HasUnitLevelSecurity = true;
            _table.HasUnitLevelSecurity = true;

            Assert.IsTrue(_table.HasUnitLevelSecurity);
        }


        /// <summary>
        ///   Granting select permission after unit level security was turned off.
        /// </summary>
        [TestMethod]
        public void GrantingSelectAfterSecurityWasTurnedOff()
        {
            _table.HasUnitLevelSecurity = false;

            _table.Grant(_user, DataAction.Select);

            Assert.AreEqual(_userTable.RowCount, 1);
        }


        /// <summary>
        ///   Granting select permission before unit level security was turned off.
        /// </summary>
        [TestMethod]
        public void GrantingSelectBeforeSecurityWasTurnedOff()
        {
            _table.Grant(_user, DataAction.Select);

            _table.HasUnitLevelSecurity = false;

            Assert.AreEqual(_userTable.RowCount, 1);
        }


        /// <summary>
        ///   Granting partial select permission after unit level security was turned off.
        /// </summary>
        [TestMethod]
        public void GrantingPartialSelectAfterSecurityWasTurnedOff()
        {
            _table.HasUnitLevelSecurity = false;

            _table.Grant(_user, DataAction.Select, AccessLevel.Unit);

            Assert.AreEqual(_userTable.RowCount, 1);
        }


        /// <summary>
        ///   Granting partial select permission before unit level security was turned off.
        /// </summary>
        [TestMethod]
        public void GrantingPartialSelectBeforeSecurityWasTurnedOff()
        {
            _table.Grant(_user, DataAction.Select, AccessLevel.Own);

            Assert.AreEqual(_userTable.RowCount, 0);

            _table.HasUnitLevelSecurity = false;

            Assert.AreEqual(_userTable.RowCount, 1);
        }


        /// <summary>
        ///   Selecting with no permission when unit level security is turned off.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(DbException), AllowDerivedTypes = true)]
        public void SelectingWithNoPermissionWhenSecurityIsTurnedOff()
        {
            _table.HasUnitLevelSecurity = false;

            // ReSharper disable UnusedVariable
            var rowCount = _userTable.RowCount;
            // ReSharper restore UnusedVariable
        }


        /// <summary>
        ///   Granting insert permission after unit level security is turned off.
        /// </summary>
        [TestMethod]
        public void GrantingInsertAfterSecurityIsTurnedOff()
        {
            _table.HasUnitLevelSecurity = false;

            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Insert);

            _userTable.InsertRow();

            Assert.AreEqual(_userTable.RowCount, 2);
        }


        /// <summary>
        ///   Granting insert permission before unit level security was turned off.
        /// </summary>
        [TestMethod]
        public void GrantingInsertBeforeSecurityWasTurnedOff()
        {
            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Insert, AccessLevel.Own);

            _table.HasUnitLevelSecurity = false;

            _userTable.InsertRow();

            Assert.AreEqual(_userTable.RowCount, 2);
        }


        /// <summary>
        ///   Insrting with no permission when unit level security
        /// </summary>
        [TestMethod]
        public void InsertingWithNoPermissionWhenSecurityWasTurnedOff()
        {
            _table.Grant(_user, DataAction.Select);

            _table.HasUnitLevelSecurity = false;

            var initialRowCount = _table.InternalRowCount;

            try
            {
                _userTable.InsertRow();
            }
            catch (DbException)
            {
                Assert.AreEqual(_table.RowCount, initialRowCount);
                return;
            }

            Assert.Fail();
        }


        /// <summary>
        ///   Granting update permission after unit level security was turned off.
        /// </summary>
        [TestMethod]
        public void GrantingUpdateAfterSecurityWasTurnedOff()
        {
            _table.HasUnitLevelSecurity = false;

            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Insert);
            _table.Grant(_user, DataAction.Update);

            var id = _userTable.InsertRow();

            _userTable.UpdateRow(id, _userTable.SystemColumns.Owner.Value("new_owner"));
        }


        /// <summary>
        ///   Granting update permission before unit level security was turned off.
        /// </summary>
        [TestMethod]
        public void GrantingUpdatePermissionBeforeSecurityWasTurnedOff()
        {
            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Insert);
            _table.Grant(_user, DataAction.Update);

            _table.HasUnitLevelSecurity = false;

            var id = _userTable.InsertRow();

            _userTable.UpdateRow(id, _userTable.SystemColumns.Owner.Value("new_owner"));
        }


        /// <summary>
        ///   Granting partial update permission after unit level security was turned off.
        /// </summary>
        [TestMethod]
        public void GrantingPartialUpdateAfterSecurityWasTurnedOff()
        {
            _table.HasUnitLevelSecurity = false;

            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Insert);
            _table.Grant(_user, DataAction.Update, AccessLevel.Own);

            var id = _userTable.InsertRow();

            _userTable.UpdateRow(id, _userTable.SystemColumns.Owner.Value("new_owner"));
        }


        /// <summary>
        ///   Granting partial update permission before unit level security was turned off.
        /// </summary>
        [TestMethod]
        public void GrantingPartialUpdatePermissionBeforeSecurityWasTurnedOff()
        {
            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Insert);
            _table.Grant(_user, DataAction.Update, AccessLevel.Unit);

            _table.HasUnitLevelSecurity = false;

            var id = _userTable.InsertRow();

            _userTable.UpdateRow(id, _userTable.SystemColumns.Owner.Value("new_owner"));
        }


        /// <summary>
        ///   Updating with no update permission when unit level security is turned off.
        /// </summary>
        [TestMethod]
        public void UpdatingWithNoPermissionWhenSecurityIsTurnedOff()
        {
            _table.HasUnitLevelSecurity = false;

            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Insert);

            var id = _userTable.InsertRow();

            try
            {
                _userTable.UpdateRow(id, _userTable.SystemColumns.Owner.Value("new_owner"));
            }
            catch (DbException)
            {
                Assert.AreEqual(_table.SystemColumns.Owner.Read(id).GetString(), _user.Name);
                return;
            }

            Assert.Fail();
        }


        /// <summary>
        ///   Granting delete permission after unit level security was turned off.
        /// </summary>
        [TestMethod]
        public void GrantingDeletePermissionAfterSecurityWasTurnedOff()
        {
            _table.HasUnitLevelSecurity = false;

            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Insert);
            _table.Grant(_user, DataAction.Delete);

            var id = _userTable.InsertRow();

            Assert.AreEqual(_userTable.RowCount, 2);

            _userTable.DeleteRow(id);

            Assert.AreEqual(_userTable.RowCount, 1);
        }


        /// <summary>
        ///   Granting delete permission before unit level security was turned off.
        /// </summary>
        [TestMethod]
        public void GrantingDeletePermissionBeforeSecurityWasTurnedOff()
        {
            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Insert);
            _table.Grant(_user, DataAction.Delete);

            _table.HasUnitLevelSecurity = false;

            var id = _userTable.InsertRow();

            Assert.AreEqual(_userTable.RowCount, 2);

            _userTable.DeleteRow(id);

            Assert.AreEqual(_userTable.RowCount, 1);
        }


        /// <summary>
        ///   Granting partial delete permission after unit level security was turned off.
        /// </summary>
        [TestMethod]
        public void GrantingPartialDeletePermissionAfterSecurityWasTurnedOff()
        {
            _table.HasUnitLevelSecurity = false;

            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Insert);
            _table.Grant(_user, DataAction.Delete, AccessLevel.ParentUnits);

            var id = _table.InsertRow();

            Assert.AreEqual(_userTable.RowCount, 2);

            _userTable.DeleteRow(id);

            Assert.AreEqual(_userTable.RowCount, 1);
        }


        /// <summary>
        ///   Granting partial delete permission before unit level security was turned off.
        /// </summary>
        [TestMethod]
        public void GrantingPartialDeletePermissionBeforeSecurityWasTurnedOff()
        {
            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Insert);
            _table.Grant(_user, DataAction.Delete, AccessLevel.Own);

            _table.HasUnitLevelSecurity = false;

            var id = _table.InsertRow();

            Assert.AreEqual(_userTable.RowCount, 2);

            _userTable.DeleteRow(id);

            Assert.AreEqual(_userTable.RowCount, 1);
        }


        /// <summary>
        ///   Deleting with no delete permission after unit level security was turned off.
        /// </summary>
        [TestMethod]
        public void DeletingWithNoPermissionAfterSecurityWasTurnedOff()
        {
            _table.HasUnitLevelSecurity = false;

            _table.Grant(_user, DataAction.Select);
            _table.Grant(_user, DataAction.Insert);

            var id = _table.InsertRow();

            Assert.AreEqual(_userTable.RowCount, 2);

            try
            {
                _userTable.DeleteRow(id);
            }
            catch (DbException)
            {
                Assert.AreEqual(_table.RowCount, 2);
                return;
            }

            Assert.Fail();
        }
    }
}
